package com.zenglx.assembly.avoidRepeatSubmit;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.concurrent.TimeUnit;

/**
 * 重复提交靠按钮解决不是特别靠谱
 * 使用redis分布式锁(setnx)解决
 * hash值 对象实例相对，默认的hash值相等，通过hash值作为锁值校验入参(不要随便重写request hash()方法)
 * hash值超时时间（10 s）
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitHandler {


    @Autowired
    private RedisTemplate<String, ?> redisTemplate;


    @Before("@annotation(com.zenglx.assembly.avoidRepeatSubmit.RepeatSubmit)")
    public void beforeCheck(JoinPoint joinPoint) {
        Long start = System.currentTimeMillis();
        Object[] objects = joinPoint.getArgs();
        //这种情况加入参数校验 10s过期
        if (objects.length == 1) {
            if (setNx(String.valueOf(objects[0].hashCode()))) {
                expireByMilliseconds(String.valueOf(objects[0].hashCode()), 10L);
                log.info("check time : " + (System.currentTimeMillis() - start) + "ms");
            } else {
                log.error("RepeatSubmit repeat: " + joinPoint.toLongString());
                throw new RepeatSubmitException("请不要重复提交");
            }
        }
    }
    /**
     * 利用redis
     *
     * @param key
     * @return
     */
    private boolean setNx(final String key) {
        return redisTemplate.execute((RedisCallback<Boolean>) redisConnection ->
                redisConnection.setNX(redisTemplate.getStringSerializer().serialize(key),
                        redisTemplate.getStringSerializer().serialize(key)));
    }

    /**
     * 以毫秒为单位设置key的超时时间
     *
     * @param key        key
     * @param expireTime 超时时间
     * @return boolean
     */
    private boolean expireByMilliseconds(String key, Long expireTime) {
        return redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
    }


}
